<template>
	<div ref="container" class="w100 h100 relative">
		<div style="z-index: 10" class="absolute flex-center w100 h100" v-show="loading && modelData.downloadUrl">
			<dv-decoration :scale="2">
				<span class="fw" style="font-size: 30px"> {{ progress }} %</span>
			</dv-decoration>
		</div>
		<div class="sceneChildren">
			<el-tree
				:data="sceneChildren"
				:props="{
					children: 'children',
					label: 'type',
					class: 'sceneChildrenTree'
				}"
				highlight-current
				node-key="uuid"
			/>
		</div>
		<div
			style="z-index: 10; bottom: 0; right: 0; width: 128px; height: 128px; position: absolute"
			@pointerup.stop="(e) => viewHelper.handleClick(e)"
			@pointerdown.stop=""
		/>
	</div>
</template>

<script setup>
// 引入资源
import { Sky } from 'three/examples/jsm/objects/Sky';
import {
	useResizeObserver,
	THREE,
	clock,
	GLTFloaderFn,
	OrbitControls,
	rendererFn,
	setContent,
	mixersFn,
	ViewHelper
} from '@u/threeMinx';

/**
 * url 模型地址
 */
const props = defineProps({
	modelData: {
		type: [Object],
		default: () => {}
	}
});

let container = shallowRef(null), // 模型渲染的Demo节点
	renderer, //  WebGLRenderer渲染器
	scene, // 场景
	camera, // 相机
	controls, // 控制器
	viewHelper, // 视角
	loading = ref(false),
	progress = ref(0);

// 组件宽高
let width, height;
useResizeObserver(container, (entries) => {
	width = entries[0].contentRect.width;
	height = entries[0].contentRect.height;
	camera.aspect = width / height;
	camera.updateProjectionMatrix();
	renderer.setSize(width, height);
});

// 初始化生命周期
onMounted(() => {
	init();
});

onBeforeUnmount(() => {
	cancelAnimationFrame(requestAnimationFrameIndex);
	// 释放显存
	renderer?.dispose();
	// 释放内存
	scene?.clear();
	container = null;
	scene = null;
});

// 监听模型地址变化
watch(
	() => props.modelData,
	(val) => {
		if (val) {
			clearSceneModel();
			loaderFn();
		}
	}
);

// 清除场景模型数据
function clearSceneModel() {
	// 材质释放内存
	scene.traverse((v) => {
		if (v.type === 'Mesh') {
			v.geometry.dispose();
			v.material.dispose();
		}
	});
	// 移除模型
	scene.remove(model);
	model = null;
	// 取消动画帧
	cancelAnimationFrame(requestAnimationFrameIndex);
	sceneChildren.value = [];
}

/**
 * 场景初始化
 */
function init() {
	// 场景
	scene = new THREE.Scene();

	// 相机
	camera = new THREE.PerspectiveCamera(45, container.value.offsetWidth / container.value.offsetHeight, 0.01, 2000);
	camera.position.set(0, 2, 6);
	scene.add(camera);

	// 渲染器
	renderer = rendererFn(container.value);

	// 控制器
	controls = new OrbitControls(camera, renderer.domElement);
	controls.center = new THREE.Vector3();
	controls.target.set(0, 0, 0);

	// xyz视角
	viewHelper = new ViewHelper(camera, container.value);
	viewHelper.controls = controls;

	// 灯光
	lights();

	// 天空
	skyFn();
}

// 天空
function skyFn() {
	const sky = new Sky();
	scene.add(sky);
	sky.scale.setScalar(10000);
	const skyUniforms = sky.material.uniforms;
	skyUniforms['turbidity'].value = 10;
	skyUniforms['rayleigh'].value = 2;
	skyUniforms['mieCoefficient'].value = 0.005;
	skyUniforms['mieDirectionalG'].value = 0.8;

	// 太阳
	const pmremGenerator = new THREE.PMREMGenerator(renderer),
		sun = new THREE.Vector3(),
		parameters = {
			elevation: 1,
			azimuth: 180
		},
		phi = THREE.MathUtils.degToRad(90 - parameters.elevation),
		theta = THREE.MathUtils.degToRad(parameters.azimuth);

	// 太阳的方向 取球形上的一个点
	sun.setFromSphericalCoords(1, phi, theta);
	// 或者定义方向
	// sun.set(1, 0.01, -1);
	sky.material.uniforms['sunPosition'].value.copy(sun);
	scene.environment = pmremGenerator.fromScene(sky).texture;
}

/**
 * 模型加载
 */
let model,
	mixer = [],
	sceneChildren = shallowRef([]);
function loaderFn() {
	loading.value = true;
	GLTFloaderFn.load(
		props.modelData.downloadUrl,
		(object) => {
			model = object.scene;
			// 居中
			setContent(model);
			scene.add(model);
			//动画
			mixer = mixersFn(object);
			// 渲染器
			render();
			// 场景数据
			sceneChildren.value = scene.children;
			// 重置进度
			nextTick(() => {
				loading.value = false;
				progress.value = 0;
			});
		},
		(xhr) => {
			progress.value = Math.round((xhr.loaded / props.modelData.fileSize) * 100, 2);
		}
	);
}

/**
 * 灯光
 */
function lights() {
	// 自然光
	scene.add(new THREE.AmbientLight('#ffffff', 1));
}

/***
 * requestAnimationFrame 渲染
 */
let requestAnimationFrameIndex;
function render() {
	const delta = clock.getDelta();
	requestAnimationFrameIndex = requestAnimationFrame(render);
	// 控制器
	controls?.update();
	// 运行动画
	mixer?.update(delta);
	// 渲染器渲染
	renderer?.render(scene, camera);
	// xyz视角可视化渲染
	if (viewHelper.animating === true) viewHelper.update(delta);
	renderer.autoClear = false;
	viewHelper?.render(renderer);
	renderer.autoClear = true;
}
</script>

<style lang="scss" scoped>
.sceneChildren {
	position: absolute;
	top: 0;
	right: 0;
	width: 200px;
	height: calc(100% - 128px);
	overflow: auto;
	background-color: var(--color-bg-1);
	border-radius: 4px;
	border: 1px solid var(--color-bg-3);
	z-index: 0;
	padding-right: 10px;
}
:deep() {
	.sceneChildrenTree {
		width: 400px;
		& > .el-tree-node__children {
			overflow: visible !important;
		}
	}
}
</style>
